package cn.tedu.submarine;
import javax.swing.*;
import java.awt.*;
import java.util.Arrays;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;


/**
 * 整个游戏世界
 */
public class World extends JPanel {
    public static final int WIDTH = 641;//窗口的宽
    public static final int HEIGHT = 479;//窗口的高
    /**设计整个游戏的两种状态为常量和当前状态为变量*/
    //运行状态
    public static final int RUNNING = 0;
    //游戏结束状态
    public static final int GAME_OVER = 1;
    //窗口当前状态，(默认为运行状态)
    private int state = RUNNING;

    /**创建战舰类对象*/
    //声明战舰同时赋值创建战舰类对象。
    private Battleship ship = new Battleship();
    /**创建4种潜艇类对象造型在超类数组中*/
    //潜艇数组
    private SeaObject[] submarines = {};
    /**创建水雷数组*/
    private Mine[] mines = {};
    /**创建炸弹数组*/
    private Bomb[] bombs = {};
    /**重写画对象的方法*/
    public void paint(Graphics g){
        //根据当前状态做不同的处理,只要对整数判断相等,用switch..case结构更简洁。
        switch (state){
            //游戏结束时画游戏结束图
            case GAME_OVER :
                //画游戏结束图
                Images.gameover.paintIcon(null,g,0,0);
                //break是跳出switch的,switch跳出去,paint方法就结束了
                break;
            //运行时
            case RUNNING:
                //画海洋图
                Images.sea.paintIcon(null,g,0,0);
                //画战舰
                ship.paintImage(g);
                //画潜艇,遍历数组把4个潜艇都画出来
                for (int i=0;i< submarines.length;i++){
                    submarines[i].paintImage(g);
                }
                //画水雷,遍历水雷数组
                for (int i=0;i< mines.length;i++){
                    mines[i].paintImage(g);
                }
                //画炸弹,遍历炸弹数组
                for (int i=0;i< bombs.length;i++){
                    bombs[i].paintImage(g);
                }
                /*画得分和得命的在窗口中的字幕*/
                //画一个字符串里面传三个参数，字符串是什么score的值是几，后面两个是坐标。
                g.drawString("SCORE:"+score,200,50);
                //一调用getLife()方法就得到命数了
                g.drawString("LIFE:"+ship.getLife(),400,50);
        }
    }
    /**生成潜艇(侦察潜艇、鱼雷潜艇、水雷潜艇、核潜艇)对象*/
    public SeaObject nextSubmarine(){
        Random rand = new Random();//随机数对象
        int type = rand.nextInt(25);//0到19之间的随机数
        if (type<10) {//0到9时，返回侦察潜艇对象
            return new ObserveSubmarine();
        }else if (type<15){//10-14时，能走到这一定>=10，返回鱼雷潜艇。
            return new TorpedoSubmarine();
        } else if (type<20) {//15-19时，返回水雷潜艇对象
            return new MineSubmarine();
        }else{//20-24时，返回核潜艇对象
            return new NuclearSubmarine();
        }
    }
    /**控制潜艇入场的计数器*/
    private int subEnterIndex = 0;
    /**4种潜艇入场的方法*/
    private void submarineEnterAction() {
        subEnterIndex++;
        if (subEnterIndex%40==0){
            //此处多长时间走一次，每400毫秒走一次。
            SeaObject obj = nextSubmarine();//在同一个类中可以直接访问不用打点。
            submarines = Arrays.copyOf(submarines,submarines.length+1);
            submarines[submarines.length-1] = obj;//将obj添加到submarines数组的最后一个元素上。
        }
    }
    //水雷入场计数
    private int mineEnterIndex = 0;
    /**水雷入场的方法*/
    //每10毫秒走一次
    private void mineEnterAction() {
        //每10毫秒增1
        mineEnterIndex++;
        //每1000(100*10)毫秒走一次
        if (mineEnterIndex%100==0){
            //for循环遍历潜艇数组
            for (int i=0;i<submarines.length;i++){
                //判断若潜艇为水雷潜艇
                if (submarines[i] instanceof MineSubmarine){
                    //则将潜艇强转为水雷潜艇
                    MineSubmarine ms = (MineSubmarine) submarines[i];
                    //获取水雷对象,让水雷艇发射水雷存到obj里面装到mines数组中。
                    Mine obj = ms.shootMine();
                    //扩容,在元素末尾补默认值null,把炸弹装到末尾
                    mines = Arrays.copyOf(mines,mines.length+1);
                    //将obj添加到最后一个元素上
                    mines[mines.length-1] = obj;
                }
            }
            //for循环遍历潜艇数组
            for (int i=0;i<submarines.length;i++){
                //判断若潜艇为核潜艇潜艇
                if (submarines[i] instanceof NuclearSubmarine){
                    //则将潜艇强转为水雷潜艇
                    NuclearSubmarine ms = (NuclearSubmarine) submarines[i];
                    //获取水雷对象,让水雷艇发射水雷存到obj里面装到mines数组中。
                    Mine obj1 = ms.shootNuclear();
                    //扩容,在元素末尾补默认值null,把炸弹装到末尾
                    mines = Arrays.copyOf(mines,mines.length+1);
                    //将obj添加到最后一个元素上
                    mines[mines.length-1] = obj1;
                }
            }
        }
    }
    /**海洋对象移动的方法*/
    private void moveAction() {//每10毫秒走一次
        for (int i=0;i<submarines.length;i++){
            submarines[i].move();//潜艇移动
        }
        for (int i=0;i< mines.length;i++){
            mines[i].move();//水雷移动
        }
        for (int i=0;i< bombs.length;i++){
            bombs[i].move();//炸弹移动
        }
    }
    /**删除越界的海洋对象的方法*/
    private void outOfBoundsAction() {
        //遍历潜艇数组
        for (int i=0;i<submarines.length;i++){
            //判断潜艇是否越界或者死了
            if (submarines[i].isOutOfBounds() || submarines[i].isDead()){
                //修改越界元素为最后一个元素
                submarines[i] = submarines[submarines.length-1];
                //缩容
                submarines = Arrays.copyOf(submarines,submarines.length-1);
            }
        }
        //遍历水雷数组
        for (int i=0;i<mines.length;i++){
            //判断水雷是否越界或水雷状态是死了的
            if (mines[i].isOutOfBounds() || mines[i].isDead()){
                //修改越界元素为最后一个元素
                mines[i] = mines[mines.length-1];
                //缩容
                mines = Arrays.copyOf(mines,mines.length-1);
            }
        }
        //遍历炸弹数组
        for (int i=0;i<bombs.length;i++){
            //判断炸弹是否越界或炸弹状态是死了的
            if (bombs[i].isOutOfBounds() || bombs[i].isDead()){
                //修改越界元素为最后一个元素
                bombs[i] = bombs[bombs.length-1];
                //缩容
                bombs = Arrays.copyOf(bombs,bombs.length-1);
            }
        }
    }
    //定义玩家得分的变量
    private int score = 0;
    /**深水炸弹与潜艇的碰撞的方法*/
    private void bombBangAction() {
        //遍历炸弹数组
        for (int i=0;i< bombs.length;i++){
            //获取炸弹数组的每个对象用炸弹类型接收
            Bomb b = bombs[i];
            //遍历潜艇数组
            for (int j=0;j<submarines.length;j++){
                //获取潜艇数组的每个对象用SeaObject超类接收
                SeaObject s = submarines[j];
                //判断若炸弹对象活着 并且 潜艇对象活着 并且 检测潜艇与炸弹碰撞
                //若三者条件满足,说明撞上了
                if (b.isLive() && s.isLive() && s.isHit(b)){
                    //则潜艇去死
                    s.goDead();
                    //炸弹去死
                    b.goDead();
                    //若被撞潜艇为分,这里为true意味着强转能成功
                    //判断引用所指向的对象是否是得分接口的类型.
                    //如果被撞的潜艇对象继承了得分接口,则为true匹配.
                    if (s instanceof EnemyScore){
                        //将被撞潜艇强转为得分接口
                        EnemyScore es = (EnemyScore) s;
                        //玩家得分,调用接口里面的getScore方法。
                        score += es.getScore();
                    }
                    //若被撞潜艇为命,这里为true意味着强转能成功
                    //判断引用所指向的对象是否是得命接口的类型.
                    //如果被撞的潜艇对象继承了得命接口,则为true匹配.
                    if (s instanceof EnemyLife){
                        //将被撞潜艇强转为得命接口
                        EnemyLife el = (EnemyLife) s;
                        //获取命数
                        int num = el.getLife();
                        //战舰增命
                        ship.addLife(num);
                    }
                }
            }
        }
    }
    /**水雷与战舰的碰撞方法*/
    private void mineBangAction() {
        //遍历水雷数组
        for (int i=0;i<mines.length;i++){
            //获取每一个水雷,现在水雷变量m有了,战舰就是ship
            Mine m = mines[i];
            //在比之前为了保证健壮性,判断了代码更严谨。
            // 先判断水雷是否活着 并且 战舰是否活着 并且水雷是否撞上战舰了。
            if (m.isLive() && ship.isLive() && m.isHit(ship)){
                //如果活着还撞上了，水雷去死
                m.goDead();
                //如果活着还撞上了，战舰减命
                ship.subtractLife();
            }
        }
    }
    /**检测游戏结束的方法*/
    //每10毫秒检测，结束马上停
    private void checkGameOverAction() {
        //判断战舰的命数小于等于0，表示游戏结束，则贴一张gameover结束图。
        if (ship.getLife()<=0){
            //将窗口状态设置为GAME_OVER游戏结束状态
            state = GAME_OVER;
        }
    }
    /**程序启动执行的方法*/
    public void action(){
        //侦听器
        KeyAdapter k = new KeyAdapter() {
            /**重写keyReleased()按键抬起事件的方法*/
            @Override
            public void keyReleased(KeyEvent e) {
                //当按键抬起时会自动执行,现在这里是只要按任意键抬起就触发事件，
                // 而发炸弹是按空格键。所以if判断是否是空格
                //e.获取得到按键编码的方法==参数KeyEvent类的类型.SPACE就是空格键
                if (e.getKeyCode()==KeyEvent.VK_SPACE){
                    //获取深水炸弹对象
                    Bomb obj = ship.shootBomb();
                    //炸弹数组扩容,数组长度+1，所以接下来把炸弹装到末尾
                    bombs = Arrays.copyOf(bombs,bombs.length+1);
                    //将obj添加到炸弹数组的最后一个元素上
                    bombs[bombs.length-1] = obj;
                }
                //若按键是左键头则触发战舰左移
                if (e.getKeyCode()==KeyEvent.VK_LEFT){
                    ship.moveLeft();
                }
                //若按键是右键头则触发战舰右移
                if (e.getKeyCode()==KeyEvent.VK_RIGHT){
                    ship.moveRight();
                }
            }
        };
        //添加侦听，this指窗口，相当于把侦听器加到窗口上了。
        this.addKeyListener(k);
        //定时器
        Timer timer = new Timer();
        int interval = 10;//定时间隔(以毫秒为单位)
        timer.schedule(new TimerTask() {
            @Override
            public void run() {//定时干的事,每10毫秒自动调用run方法
                submarineEnterAction();//潜艇入场的方法
                mineEnterAction();//水雷入场的方法
                moveAction();//海洋对象移动的方法
                outOfBoundsAction();//删除越界海洋对象的方法
                bombBangAction();//炸弹和潜艇的碰撞方法
                mineBangAction();//水雷与战舰的碰撞方法
                checkGameOverAction();//检测游戏结束的方法
                repaint();//这个方法是超类里的，重画，系统重新调用paint()方法。
            }
        },interval,interval);//定时计划表
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        World world = new World();
        world.setFocusable(true);
        frame.add(world);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(WIDTH + 16, HEIGHT + 39);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        world.action();
    }
}
/*
SeaObject[] submarines = new SeaObject[4];
        submarines[0] = new ObserveSubmarine();//把侦查潜艇造型为超类型.装到超类数组中
        submarines[1] = new NuclearSubmarine();//把核潜艇造型为超类型.装到超类数组中
        submarines[2] = new TorpedoSubmarine();//把鱼雷潜艇造型为超类型.装到超类数组中
        submarines[3] = new MineSubmarine();//把水雷潜艇造型为超类型.装到超类数组中
        for (int i=0;i< submarines.length;i++){//遍历超类数组中每个元素获取数组的长度(元素的个数)
            SeaObject s = submarines[i];//遍历获取数组的每个元素，用SeaObject超类型接收.
            System.out.println(s);//输出遍历的元素类型.
            System.out.println(s.x+","+s.y);//输出每个潜艇的x和y坐标
            s.move();//调用每个元素类型的move()方法.
        }
 */